#!/usr/bin/perl

use strict;
use warnings;

use Unix::PID '/var/run/hackstop.pid';
use File::Tail::App qw(tail_app);
use Time::Local;
use POSIX qw(strftime);

##
## Copyright 2006 Hugh Reid
##
## Licensed under the Apache License, Version 2.0 (the "License");
## you may not use this file except in compliance with the License.
## You may obtain a copy of the License at
##
##     http://www.apache.org/licenses/LICENSE-2.0
##
## Unless required by applicable law or agreed to in writing, software
## distributed under the License is distributed on an "AS IS" BASIS,
## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
## See the License for the specific language governing permissions and
## limitations under the License.
##

my %db = ();
my @now = localtime(time());
my $hour = $now[2];

my $adminemail = "root\@localhost";
my $abuseemail = "abuse\@myisp.net";
my $companyname = "Your Company Name";
my $ourip = "0.0.0.0 www.mydomain.com";
my $reportlimit = 10;
my $banlimit = 20;
my $contactphone = "000000000000";

## This tails the appropriate log
tail_app({
	'new'          => ['/var/log/secure'],
	##'new'          => ['/root/test.log'],
	'line_handler' => \&linehandler,
});

## This is called on every new line added to the log
sub linehandler {
	my($line) = @_;
	
	## This matches the hack attempt
	if ($line =~ m/Failed password/) {
		if ($line =~ m/^(\w+\s+\w+\s+..:..:..).* for (.+) from ::ffff:(\S+)/) {
			my $when = $1;
			my $user = $2;
			my $ip = $3;

			## add exempt user names here
			#if ($user !~ /myuser/) {
			
			hackattempt($when, $user, $ip);
			
			#}
		}
	}
}

## Having identified a hack attempt then process the reporting
sub hackattempt {
	my($when, $user, $ip) = @_;
	my @now = localtime(time());
	my $tz = strftime("%z", @now);
	
	## record the evidence in the db
	$db{'evidence'}{$ip}{$when . " " . $tz} = $user;

	## users normally have a 1/1 relationship with their ip
	$db{'users'}{$ip}{$user} = 0;

	## record how many attempts from an ip in this hour
	my $thishour = $now[2];
	if ($thishour != $hour) {
		$hour = $thishour;
		delete $db{'hourlycount'};
	}
	$db{'hourlycount'}{$ip}++;
	
	## check for the reporting criteria
	my @users = keys(%{$db{'users'}{$ip}});
	if ($db{'hourlycount'}{$ip} == $reportlimit || length(@users) == $reportlimit) {
		if (!exists($db{'reported'}{$ip})) {
			report($ip);
			$db{'reported'}{$ip}++;
		}
	}
	
	## check for the banning criteria
	if ($db{'hourlycount'}{$ip} == $banlimit || length(@users) == $banlimit) {
		ban($ip, $when);

		$db{'hourlycount'}{$ip} = 0;
		delete $db{'users'}{$ip};
	}
}

## ban the ip address
sub ban {
	my($ip, $when) = @_;
	
	## read in the current file
	open(DAT, "/etc/hosts.deny") || die("Could not open file!");
	my $current_contents = "";
	while (my $line = <DAT>) {
		$current_contents = $current_contents . $line;
   	}
	close(DAT);
	
	## check that the current file does not alreay refer to the ip
	if ($current_contents !~ /$ip/) {
		open(APPEND, ">>/etc/hosts.deny");
		print APPEND "ALL: $ip \t# banned by $0 at $when \n";
		close(APPEND);
	}
}

## report the attempt
sub report {
	my($ip) = @_;
	
	## pick up the evidence as of now
	my @events = keys(%{$db{'evidence'}{$ip}});

	## send an email report
	if (open(SENDMAIL, "|/usr/sbin/sendmail -t")) {
		print SENDMAIL "Reply-to: $adminemail\n";
		print SENDMAIL "Subject: Attack from $ip\n";
		print SENDMAIL "To: $abuseemail\n";
		print SENDMAIL "Cc: $adminemail\n";
		print SENDMAIL "Content-type: text/plain\n\n";
		print SENDMAIL "This is an automated message from $companyname.\n\n";
		print SENDMAIL "There is in progress a systematic attack against our systems\ncoming from:\n\n$ip\n\n";
		print SENDMAIL "Our IP address is $ourip\n";
		print SENDMAIL "This message is sent when the number of suspicious login attempts passes $reportlimit.\n";
		print SENDMAIL "Our automated systems will take action when the number of attempts pass $banlimit.\n";
		print SENDMAIL "So you have a brief period to gather further evidence.\n";
		print SENDMAIL "Here are the details of the attack so far:\n";
		foreach (sort @events) {
			print SENDMAIL "    $ip   at   $_   as user $db{'evidence'}{$ip}{$_}\n";
		}
		print SENDMAIL "\n";
		print SENDMAIL "Server time is now: " . scalar(gmtime) . " GMT\n";
		print SENDMAIL "\n";
		print SENDMAIL "If you wish to discuss this email then call $contactphone during office hours.\n";
		print SENDMAIL "\n";
		print SENDMAIL "Thanks\n\n";
		print SENDMAIL "Powered by http://code.google.com/p/hackstop/\n";
		close(SENDMAIL);
	}
	
}


